
#include "../iface/debug.h"
#include "../iface/APRInterface.h"
#include "TCPClient.h"
#include <apr_network_io.h>

// TCPClient using blocking sockets so that the while (1) loops don't spin!

TCPClient::TCPClient(const char *ihostname, int iportno, apr_pool_t *imp)
{
   int isinvalid = 0;
   int hassocket = 0;
   int hasmp = 0;
   apr_status_t status;
   status = apr_pool_create(&mp, imp);
   CheckAPRError(status); isinvalid |= status;
   hasmp = 1;

   status = apr_sockaddr_info_get(&addrstruct, ihostname, APR_INET, iportno, 0, mp);
   CheckAPRError(status); isinvalid |= status; if (isinvalid) goto failed;
   status = apr_socket_create(&tsocket, addrstruct->family, SOCK_STREAM, APR_PROTO_TCP, mp);
   CheckAPRError(status); isinvalid |= status; if (isinvalid) goto failed;
   hassocket = 1;
   status = apr_socket_connect(tsocket, addrstruct);
   CheckAPRError(status); isinvalid |= status; if (isinvalid) goto failed;
   status = apr_socket_opt_set(tsocket, APR_SO_NONBLOCK, 0);
   CheckAPRError(status); isinvalid |= status;
   status = apr_socket_opt_set(tsocket, APR_TCP_NODELAY, 0);
   CheckAPRError(status); isinvalid |= status;
   status = apr_socket_timeout_set(tsocket, CLIENT_SOCKET_TIMEOUT * 1000);
   CheckAPRError(status); isinvalid |= status;
   if (!isinvalid)
   {
      clientbb = new APRBucketBrigade(mp);
      clientbb->AddSocketBucket(tsocket);
      clientbb->WriteEOS();
   }
failed:
   if (isinvalid)
   {
      if (hassocket)
      {
         status = apr_socket_close(tsocket);
         CheckAPRError(status);
      }
      clientbb = NULL;
      if (hasmp)
      {
         apr_pool_clear(mp);
         apr_pool_destroy(mp);
      }
      mp = NULL;
   }
   isvalid = !isinvalid;
}

TCPClient::~TCPClient()
{
   if (mp == NULL) return;
   apr_status_t status;
   if (isvalid)
   {
      status = apr_socket_close(tsocket);
      CheckAPRError(status);
      if (clientbb) delete clientbb;
      apr_pool_clear(mp);
      apr_pool_destroy(mp);
      mp = NULL;
   }
}

int TCPClient::SendBuffer(const char *buf, int buflen)
{
   if (!isvalid) return 0;
   apr_status_t status;
   apr_size_t sent = 0;
   int newlen = (buflen == -1) ? strlen(buf) : buflen;
   while (1)
   {
      apr_size_t len = newlen - sent;
      if (len == 0) break;
      status = apr_socket_send(tsocket, &(buf[sent]), &len);
      CheckAPRError(status);
      if (status != 0) break;
      sent += len;
   }
   return sent;
}

int TCPClient::SendBucketBrigade(APRBucketBrigade *aprbb)
{
   if (!isvalid) return 0;
   apr_status_t status;
   apr_size_t sent = 0;
   char *buf = (char *)malloc(sizeof(char)*MAX_BLOCK_SIZE);
   while (1)
   {
      apr_size_t len = MAX_BLOCK_SIZE;
      apr_size_t newlen = aprbb->Read(buf, len);
      if ((newlen == 0) && (aprbb->IsClosed())) break;
      apr_size_t tsent = 0;
      while (1)
      {
         apr_size_t newlen2 = newlen - tsent;
         if (newlen2 == 0) break;
         status = apr_socket_send(tsocket, &(buf[tsent]), &newlen2);
         CheckAPRError(status);
         if (status != 0) break;
         tsent += newlen2;
      }
      if (status != 0) break;
      aprbb->Delete(newlen);
      sent += newlen;
   }
   free(buf);
   return sent;
}

std::vector<char> *TCPClient::RecvBuffer(int len)
{
   if (!isvalid) return 0;
   int actualreq;
   std::vector<char> *returned = new std::vector<char>();
   char *buf = (char *)malloc(sizeof(char)*MAX_BLOCK_SIZE);
   while (1)
   {
      actualreq = (MAX_BLOCK_SIZE < (len - returned->size())) ? MAX_BLOCK_SIZE : (len - returned->size());
      if (len == -1) actualreq = MAX_BLOCK_SIZE;
      apr_size_t len = clientbb->Pop(buf, actualreq);
      if (clientbb->IsClosed()) break;
      for (int i = 0; i < (int)len; i++)
         returned->push_back(buf[i]);
   }
   free(buf);
   return returned;
}

std::vector<char> *TCPClient::SendAndRecvBuffer(const char *buf, int sendlen, int recvlen)
{
   if (!isvalid) return 0;
   int newlen = (sendlen == -1) ? strlen(buf) : sendlen;
   int len2 = SendBuffer(buf, newlen);
   if (newlen != len2)
   {
      errprint("Could not complete data sending");
      return NULL;
   }
   std::vector<char> *returned = RecvBuffer(recvlen);
   return returned;
}
